#include "ast.h"
#include "parser.hpp"
#include <cmath>
#include <iostream>
#include <memory>
#include <unordered_map>
#include <unordered_set>
#include <vector>

std::shared_ptr<Ast> Ast::nullAst;
std::unordered_map<std::string, std::shared_ptr<Symbol>> Symbol::symtab;
int Ast::level;
int Ast::dump;

void Ast::dumpAst(const std::shared_ptr<Ast> &root) {
    auto echoDot = [](int level) {
        for (int i = 0; i < level * 4; i++) {
            std::cout << ".";
        }
    };
    echoDot(level);
    std::cout << "# " << root.use_count() << std::endl;
    if (auto x = dynamic_pointer_cast<Statement>(root)) {
        dumpAst(x->stmt);
    } else if (auto x = dynamic_pointer_cast<Expression>(root)) {
        echoDot(level);
        std::cout << "Expression," << root.use_count() << " : {" << std::endl;
        if (x->type == 0) {
            level++;
            dumpAst(x->left);
            level--;
        } else {
            level++;
            echoDot(Ast::level);
            std::cout << "ops: " << Expression::type2String[x->type] << ","
                      << std::endl;
            echoDot(Ast::level);
            std::cout << "left: {" << std::endl;
            level++;
            dumpAst(x->left);
            level--;
            echoDot(Ast::level);
            std::cout << "}," << std::endl;
            if (x->right != nullAst) {
                echoDot(Ast::level);
                std::cout << "right: {" << std::endl;
                level++;
                dumpAst(x->right);
                level--;
                echoDot(Ast::level);
                std::cout << "}," << std::endl;
            }
            level--;
        }
        echoDot(level);
        std::cout << "}," << std::endl;
    } else if (auto x = dynamic_pointer_cast<Fncall>(root)) {
        echoDot(level);
        std::cout << "Fncall," << root.use_count() << " : {" << std::endl;
        level++;
        echoDot(level);
        std::cout << "functype: " << x->functype << "," << endl;
        dumpAst(x->param);
        level--;
    } else if (auto x = dynamic_pointer_cast<Ufncall>(root)) {
        echoDot(level);
        std::cout << "Ufncall," << root.use_count() << " : {" << std::endl;
        level++;
        echoDot(level);
        std::cout << "functype: {" << std::endl;
        dumpAst(x->s);
        echoDot(level);
        std::cout << "}," << std::endl;
        dumpAst(x->param);
        level--;
    } else if (auto x = dynamic_pointer_cast<Flow>(root)) {
        echoDot(level);
        std::cout << "Flow," << root.use_count() << " : {" << std::endl;

        std::string condType = x->ft == Flow::flowType::WHILE ? "while" : "if";
        level++;
        echoDot(level);
        std::cout << "cond(" << condType << "): {" << std::endl;
        level++;
        dumpAst(x->cond);
        level--;
        echoDot(level);
        std::cout << "}," << std::endl;

        echoDot(level);
        std::cout << "THEN(" << condType << "): {" << std::endl;
        level++;
        dumpAst(x->tl);
        level--;
        echoDot(level);
        std::cout << "}," << std::endl;

        if (x->el != nullAst) {
            echoDot(level);
            std::cout << "ELSE(" << condType << "): {" << std::endl;
            level++;
            dumpAst(x->el);
            level--;
            echoDot(level);
            std::cout << "}," << std::endl;
        }
        level--;
        echoDot(level);
        std::cout << "}," << std::endl;
    } else if (auto x = dynamic_pointer_cast<Numval>(root)) {
        echoDot(Ast::level);
        std::cout << "Numval," << root.use_count() << " : ";
        std::cout << x->number << "," << std::endl;
    } else if (auto x = dynamic_pointer_cast<Symbol>(root)) {
        if (x->func != nullAst) {
            echoDot(level);
            std::cout << "Symbol," << root.use_count() << " : {" << std::endl;

            level++;
            echoDot(level);
            std::cout << "name," << root.use_count() << " : ";
            std::cout << x->name << "," << std::endl;

            if (x->param != nullAst) {
                echoDot(level);
                std::cout << "param : {" << std::endl;
                level++;
                dumpAst(x->param);
                level--;
                echoDot(level);
                std::cout << "}," << std::endl;
            }
            echoDot(level);
            std::cout << "func : {" << std::endl;
            level++;
            dumpAst(x->func);
            level--;
            echoDot(level);
            std::cout << "}," << std::endl;
            level--;

            echoDot(level);
            std::cout << "}," << std::endl;
        } else {
            echoDot(level);
            std::cout << "Symbol," << root.use_count() << " : ";
            std::cout << x->name << "," << std::endl;
        }
    } else if (auto x = dynamic_pointer_cast<StatementList>(root)) {
        echoDot(level);
        std::cout << "StatementList," << root.use_count() << " : ["
                  << std::endl;
        for (auto &stmt : x->list) {
            dumpAst(stmt);
        }
        echoDot(level);
        std::cout << "]," << std::endl;
    } else if (auto x = dynamic_pointer_cast<ExpressionList>(root)) {
        echoDot(level);
        std::cout << "ExpressionList," << root.use_count() << " : ["
                  << std::endl;
        for (auto &stmt : x->list) {
            level++;
            dumpAst(stmt);
            level--;
        }
        echoDot(level);
        std::cout << "]," << std::endl;
    } else if (auto x = dynamic_pointer_cast<SymbolList>(root)) {
        echoDot(level);
        std::cout << "SymbolList," << root.use_count() << " : [" << std::endl;
        for (auto &stmt : x->list) {
            level++;
            dumpAst(stmt);
            level--;
        }
        echoDot(level);
        std::cout << "]," << std::endl;
    }
}

Ast::~Ast() {
    if (auto x = dynamic_cast<StatementList *>(this)) {
        std::cout << "StatementList Release" << std::endl;
    } else if (auto x = dynamic_cast<ExpressionList *>(this)) {
        std::cout << "ExpressionList Release" << std::endl;
    } else if (auto x = dynamic_cast<SymbolList *>(this)) {
        std::cout << "SymbolList Release" << std::endl;
    } else if (auto x = dynamic_cast<Statement *>(this)) {
        std::cout << "Statement Release" << std::endl;
    } else if (auto x = dynamic_cast<Expression *>(this)) {
        std::cout << "Expression Release" << std::endl;
    } else if (auto x = dynamic_cast<Symbol *>(this)) {
        std::cout << "Symbol Release" << std::endl;
    } else if (auto x = dynamic_cast<Fncall *>(this)) {
        std::cout << "Fncall Release" << std::endl;
    } else if (auto x = dynamic_cast<Ufncall *>(this)) {
        std::cout << "Ufncall Release" << std::endl;
    } else if (auto x = dynamic_cast<Flow *>(this)) {
        std::cout << "Flow Release" << std::endl;
    } else if (auto x = dynamic_cast<Numval *>(this)) {
        std::cout << "Numval Release" << std::endl;
    }
}

double Ast::eval(const std::shared_ptr<Ast> &root) {
    double v = 0;
    if (root == nullAst) {
        return 0.0;
    }
    if (auto x = dynamic_pointer_cast<Statement>(root)) {
        v = eval(x->stmt);
    } else if (auto x = dynamic_pointer_cast<Expression>(root)) {
        if (x->type == 0) {
            v = eval(x->left);
        } else {
            switch (x->type) {
            case Marker::Parser::token::TOKEN_EQL:
                v = dynamic_pointer_cast<Symbol>(x->left)->value =
                    eval(x->right);
                break;
            case Marker::Parser::token::TOKEN_LOR:
                v = eval(x->left) || eval(x->right);
                break;
            case Marker::Parser::token::TOKEN_LAND:
                v = eval(x->left) && eval(x->right);
                break;
            case 1:
                v = eval(x->left) > eval(x->right);
                break;
            case 2:
                v = eval(x->left) < eval(x->right);
                break;
            case 3:
                v = eval(x->left) != eval(x->right);
                break;
            case 4:
                v = eval(x->left) == eval(x->right);
                break;
            case 5:
                v = eval(x->left) >= eval(x->right);
                break;
            case 6:
                v = eval(x->left) <= eval(x->right);
                break;
            case Marker::Parser::token::TOKEN_ADD:
                v = eval(x->left) + eval(x->right);
                break;
            case Marker::Parser::token::TOKEN_SUB:
                v = eval(x->left) - eval(x->right);
                break;
            case Marker::Parser::token::TOKEN_MUL:
                v = eval(x->left) * eval(x->right);
                break;
            case Marker::Parser::token::TOKEN_DIV:
                v = eval(x->left) / eval(x->right);
                break;
            case Marker::Parser::token::TOKEN_ABS:
                v = fabs(eval(x->left));
                break;
            case Marker::Parser::token::TOKEN_UMINUS:
                v = -eval(x->left);
                break;
            default:
                std::cout << "Error operator" << std::endl;
                break;
            }
        }
    } else if (auto x = dynamic_pointer_cast<Fncall>(root)) {
        double v = eval(x->param);
        switch (x->functype) {
        case Fncall::bifs::B_exp:
            return sqrt(v);
        case Fncall::bifs::B_log:
            return log(v);
        case Fncall::bifs::B_print:
            std::cout << " B_print = " << v << endl;
            return v;
        case Fncall::bifs::B_sqrt:
            return sqrt(v);
        }
    } else if (auto x = dynamic_pointer_cast<Ufncall>(root)) {
        if (x->s->func == nullAst) {
            std::cout << "call to undefined function " << x->s->name
                      << std::endl;
            return 0;
        }
        if (x->param->list.size() < x->s->param->list.size()) {
            std::cout << "two few args in call to " << x->s->name << std::endl;
            return 0;
        }
        std::vector<double> oldVal;
        std::vector<double> newVal;
        for (auto &exp : x->param->list) {
            newVal.emplace_back(eval(exp));
        }
        for (int i = 0; i < x->s->param->list.size(); i++) {
            auto symTmp = dynamic_pointer_cast<Symbol>(x->s->param->list[i]);
            oldVal.emplace_back(symTmp->value);
            symTmp->value = newVal[i];
        }
        v = eval(x->s->func);
        for (int i = 0; i < x->s->param->list.size(); i++) {
            auto symTmp = dynamic_pointer_cast<Symbol>(x->s->param->list[i]);
            symTmp->value = oldVal[i];
        }
    } else if (auto x = dynamic_pointer_cast<Flow>(root)) {
        if (x->ft == Flow::flowType::IF) {
            if (eval(x->cond) != 0) {
                if (x->tl != nullAst) {
                    v = eval(x->tl);
                } else {
                    v = 0;
                }
            } else {
                if (x->el != nullAst) {
                    v = eval(x->el);
                } else {
                    v = 0;
                }
            }
        } else if (x->ft == Flow::flowType::WHILE) {
            if (x->tl != nullAst) {
                std::cout << "cal WHILE tl" << std::endl;
                while (eval(x->cond) != 0) {
                    v = eval(x->tl);
                }
            }
        } else {
            std::cout << "Flow type error" << std::endl;
            v = 0;
        }
    } else if (auto x = dynamic_pointer_cast<Numval>(root)) {
        v = x->number;
    } else if (auto x = dynamic_pointer_cast<Symbol>(root)) {
        v = x->value;
    } else if (auto x = dynamic_pointer_cast<StatementList>(root)) {
        for (auto &stmt : x->list) {
            v = eval(stmt);
        }
    } else if (auto x = dynamic_pointer_cast<ExpressionList>(root)) {
        for (auto &exp : x->list) {
            v = eval(exp);
        }
    } else if (auto x = dynamic_pointer_cast<SymbolList>(root)) {
        std::cout << "SymbolList should work with func" << std::endl;
    }
    return v;
}

std::unordered_map<int, std::string> Expression::type2String = {
    {Marker::Parser::token::TOKEN_ADD, "+"},
    {Marker::Parser::token::TOKEN_SUB, "-"},
    {Marker::Parser::token::TOKEN_MUL, "*"},
    {Marker::Parser::token::TOKEN_DIV, "/"},
    {Marker::Parser::token::TOKEN_ABS, "|"},
    {Marker::Parser::token::TOKEN_EQL, "="},
    {Marker::Parser::token::TOKEN_LAND, "&&"},
    {Marker::Parser::token::TOKEN_LOR, "||"},
    {1, ">"},
    {2, "<"},
    {3, "<>"},
    {4, "=="},
    {5, ">="},
    {6, "<="},
};